package form

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"unicode"
	"yunj/pkg/yunj/app/service/admin/adminservice"
	"yunj/pkg/yunj/core/response"
	"yunj/pkg/yunj/util"

	"github.com/gin-gonic/gin"
)

type FieldInterface interface {
	GetFieldType() (typ string, err error)                             // 获取字段类型
	GetFieldId() (id string, err error)                                // 获取字段id
	SetFieldId(id string) error                                        // 设置字段id
	GetFieldTitle() (title string, err error)                          // 获取字段标题
	SetFieldTitle(title string) error                                  // 设置字段标题
	GetFieldDefault() (def any, err error)                             // 获取字段默认值
	SetFieldDefault(def any) error                                     // 设置字段默认值
	GetFieldVerify() (rule string, err error)                          // 获取字段验证规则
	SetFieldVerify(rule string) error                                  // 设置字段验证规则
	AddFieldVerify(rule string) error                                  // 增加字段验证规则
	GetFieldVerifyTitle() (title string, err error)                    // 获取字段验证标题
	SetFieldVerifyTitle(title string) error                            // 设置字段验证标题
	GetFieldReadonly() (res bool, err error)                           // 获取字段是否只读
	SetFieldReadonly(yes bool) error                                   // 设置字段是否只读
	GetFieldGrid() (res []any, err error)                              // 获取字段栅格布局
	SetFieldGrid(grid []any) error                                     // 设置字段栅格布局
	GetFieldAuth() (auth string, err error)                            // 获取字段权限
	SetFieldAuth(auth string) error                                    // 设置字段权限
	GetError() error                                                   // 获取字段错误
	GetFormId() (id string, err error)                                 // 获取表单ID
	LayModuleExtend() (string, string)                                 // 定义获取layui扩展名称和js文件路径
	LayModuleExtendJs() string                                         // 定义获取layui扩展js文件路径
	HandleAttr() error                                                 // 处理字段属性
	IsRenderCheck() bool                                               // 是否渲染判断
	RenderHandle(ctx *gin.Context, formId string) (ok bool, err error) // 渲染处理，返回是否渲染和是否有错误
}

type Field struct {
	Type        string       `json:"type"`        // 类型
	Id          string       `json:"id"`          // 字段唯一标识
	Title       string       `json:"title"`       // 标题
	Default     any          `json:"default"`     // 默认值
	Desc        string       `json:"desc"`        // 描述
	Verify      string       `json:"verify"`      // 验证规则（例："require|mobile"，必填且为手机格式）
	VerifyTitle string       `json:"verifyTitle"` // 验证标题
	Readonly    bool         `json:"readonly"`    // 只读
	Grid        []any        `json:"grid"`        // 栅格布局。超小屏幕(手机<768px)、小屏幕(平板≥768px)、中等屏幕(桌面≥992px)、大型屏幕(桌面≥1200px)、超大屏幕(桌面≥1400px)
	Auth        string       `json:"auth"`        // 权限
	FormId      string       `json:"-"`           // 表单id
	GinContext  *gin.Context `json:"-"`           // gin上下文
	ErrPrefix   string       `json:"-"`           // 错误前缀
	Err         error        `json:"-"`           // 错误

	// 父结构体属性
	fieldInstance FieldInterface // 字段实例
}

// 设置字段实例
func SetFieldInstance(f *Field, fieldInstance FieldInterface) {
	f.fieldInstance = fieldInstance

	// 获取指针的反射值
	fieldInstanceValue := reflect.ValueOf(f.fieldInstance)
	// 检查传入的是否为指针
	if fieldInstanceValue.Kind() != reflect.Ptr || fieldInstanceValue.IsNil() {
		f.SetError("请传入字段实例指针值！")
	}

	// 初始化类型
	f.Type = util.TypeName(f.fieldInstance)
}

// 处理字段属性
func (f *Field) HandleAttr() error {
	return nil
}

// 获取字段类型
func (f *Field) GetFieldType() (typ string, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	typ = f.Type
	return
}

// 获取字段id
func (f *Field) GetFieldId() (id string, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	id = f.Id
	return
}

// 设置字段id
func (f *Field) SetFieldId(id string) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.Id = id
	return
}

// 获取字段标题
func (f *Field) GetFieldTitle() (title string, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	title = f.Title
	return
}

// 设置字段标题
func (f *Field) SetFieldTitle(title string) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.Title = title
	return
}

// 获取字段默认值
func (f *Field) GetFieldDefault() (res any, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	res = f.Default
	return
}

// 设置字段默认值
func (f *Field) SetFieldDefault(def any) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.Default = def
	return
}

// 获取字段验证规则
func (f *Field) GetFieldVerify() (rule string, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	rule = f.Verify
	return
}

// 设置字段验证规则
func (f *Field) SetFieldVerify(rule string) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.Verify = rule
	return
}

// 增加字段验证规则
func (f *Field) AddFieldVerify(rule string) (err error) {
	err = f.GetError()
	if err != nil || rule == "" {
		return
	}
	verify, err := f.GetFieldVerify()
	if err != nil {
		return
	}

	rules := strings.Split(verify, "|")
	rules = append(rules, strings.Split(rule, "|")...)
	newRules := make([]string, 0, len(rules))
	for _, rv := range rules {
		rvStr := strings.TrimSpace(rv)
		if rvStr != "" {
			newRules = append(newRules, rvStr)
		}
	}
	newRules = util.SliceUnique(newRules)
	f.Verify = strings.Join(newRules, "|")
	return
}

// 获取字段验证标题
func (f *Field) GetFieldVerifyTitle() (title string, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	title = f.VerifyTitle
	return
}

// 设置字段验证标题
func (f *Field) SetFieldVerifyTitle(title string) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.VerifyTitle = title
	return
}

// 获取字段是否只读
func (f *Field) GetFieldReadonly() (res bool, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	res = f.Readonly
	return
}

// 设置字段是否只读
func (f *Field) SetFieldReadonly(yes bool) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.Readonly = yes
	return
}

// 获取字段栅格布局
func (f *Field) GetFieldGrid() (res []any, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	res = f.Grid
	return
}

// 设置字段栅格布局
func (f *Field) SetFieldGrid(grid []any) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.Grid = grid
	return
}

// 获取字段权限
func (f *Field) GetFieldAuth() (auth string, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	auth = f.Auth
	return
}

// 设置字段权限
func (f *Field) SetFieldAuth(auth string) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.Auth = auth
	return
}

// 获取表单ID
func (f *Field) GetFormId() (formId string, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	formId = f.FormId
	return
}

// 设置表单ID
func (f *Field) setFormId(formId string) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.FormId = formId
	return
}

// 设置表头实例的gin上下文
func (f *Field) setGinContext(ctx *gin.Context) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.GinContext = ctx
	return
}

// 获取字段实例的错误前缀
func (f *Field) getErrPrefix() (prefix string, err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	prefix = f.ErrPrefix
	return
}

// 设置字段实例的错误前缀
func (f *Field) setErrPrefix(prefix string) (err error) {
	err = f.GetError()
	if err != nil {
		return
	}
	f.ErrPrefix = prefix
	return
}

// 定义获取layui扩展名称和js文件路径
func (f *Field) LayModuleExtend() (name string, js string) {
	typ, _ := f.GetFieldType()
	name = "FormField" + typ
	js = f.fieldInstance.LayModuleExtendJs()
	return
}

// 定义获取layui扩展js文件路径
func (f *Field) LayModuleExtendJs() string {
	// 默认路径
	if util.PackageFullName(f.fieldInstance) == "yunj/pkg/yunj/core/builder/form/field" {
		var jsFileNameRes strings.Builder
		typ, _ := f.GetFieldType()
		for i, char := range typ {
			if i > 0 && unicode.IsUpper(char) {
				jsFileNameRes.WriteByte('-')
			}
			jsFileNameRes.WriteRune(unicode.ToLower(char))
		}
		jsFileName := jsFileNameRes.String()
		return util.YunjStaticUrl("/static/yunj/js/modules/form/field/" + jsFileName + ".min")
	}
	return ""
}

// 获取字段错误
func (f *Field) GetError() error {
	return f.Err
}

// 设置字段错误
func (f *Field) SetError(err any) error {
	if _err := f.GetError(); _err != nil {
		return _err
	}
	var resErrMsg string
	if _errMsg, ok := err.(string); ok {
		resErrMsg = _errMsg
	} else if _err, ok := err.(error); ok {
		resErrMsg = _err.Error()
	} else {
		resErrMsg = "未知错误！"
	}

	// 错误前缀
	errPrefix, _ := f.getErrPrefix()
	if errPrefix == "" {
		typ, _ := f.GetFieldType()
		errPrefix = fmt.Sprintf("字段%s[%s]", typ, f.Id)
		f.setErrPrefix(errPrefix)
	}

	if !strings.HasPrefix(resErrMsg, errPrefix) {
		resErrMsg = errPrefix + resErrMsg
	}
	newErr := errors.New(resErrMsg)
	f.Err = newErr
	return newErr
}

// 是否渲染判断
func (f *Field) IsRenderCheck() bool {
	return true
}

// 渲染处理
// @param formId: 表单id
// @return ok: 是否渲染
// @return err: 错误消息
func (f *Field) RenderHandle(ctx *gin.Context, formId string) (ok bool, err error) {
	err = f.setFormId(formId)
	if err != nil {
		return
	}
	// 设置gin上下文
	f.setGinContext(ctx)
	if err = f.GetError(); err != nil {
		return
	}
	// 处理字段属性
	err = f.fieldInstance.HandleAttr()
	if err != nil {
		err = f.SetError(err)
		return
	}
	// 判断权限
	hasAuth := adminservice.CheckAuth(ctx, f.Auth)
	if !hasAuth {
		return
	}
	// 是否渲染判断
	ok = f.fieldInstance.IsRenderCheck()
	if !ok {
		return
	}
	// 输出lay扩展
	name, js := f.LayModuleExtend()
	response.SetCtxRenderLayModuleExtend(ctx, name, js)
	return
}
